import org.apache.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;


/**
 * Created by micocube
 * ProjectName: coding
 * PackageName: com.coding.instrumentation
 * User: micocube
 * Email: ldscube@gmail.com
 * CreateTime: 2019/1/17下午3:02
 * ModifyTime: 2019/1/17下午3:02
 * Version: 0.1
 * Description:
 * <p>
 * System.out.println("agentmain load Class  :" + className);
 * Class[] classes = inst.getInitiatedClasses(loader);
 * //                 获取所有已经被初始化过了的类
 * System.out.println("initiated classes:");
 * Arrays.stream(classes).forEach(System.out::println);
 * //                  获取所有已经被加载的类
 * Class[] allLoadedClasses = inst.getAllLoadedClasses();
 * System.out.println("loaded classes:");
 * Arrays.stream(allLoadedClasses).forEach(System.out::println);
 **/
public class HotSwapAgent {
    private static Logger logger = Logger.getLogger(HotSwapAgent.class);

    private static Map<String, Content> classes = new HashMap<String, Content>();

    public static void premain(String args, Instrumentation inst)
    {
        System.out.println("PreMain Args:" + args);
    }

    public static void agentmain(String agentArgs, Instrumentation inst){

        Metric.printInfo();

        Class<?>[] allClass = inst.getAllLoadedClasses();
        String rootPath = PathUtils.getRootPath();
        Content.getAllClasses(rootPath, classes);
        logger.debug("Scan Modified Classes:\n");
        Set<String> clazz = classes.keySet();
        for(String cl : clazz){
            Content content = classes.get(cl);
            if(content.isModified()){
                logger.debug(content);
            }
        }
        for (Class<?> c : allClass) {
            //System.out.println("Loaded class:" + c.getName());
            if (classes.containsKey(c.getName())) {
                Content content = classes.get(c.getName());
                if (content.isModified()) {

                    byte[] classFile = content.getContent();
//                    if(c.getName().equals("TestService")){
                    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                    ClassReader reader = new ClassReader(classFile);
                    reader.accept(new AopClassAdapter(Opcodes.ASM5,cw), 0);
                    classFile = cw.toByteArray();
                        //new FileOutputStream("/Users/micocube/Documents/AgentMain/out/"+c.getName()+".class").write(classFile);
//                    }

                    ClassDefinition classDefinition = new ClassDefinition(c, classFile);
                    try {
                        inst.redefineClasses(classDefinition);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (UnmodifiableClassException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

//        HotSwapTransformer transformer = new HotSwapTransformer();
//        inst.addTransformer(transformer, true);
//        inst.retransformClasses(Account.class);
    }
}
